#pragma once

#include <type_traits>

#ifdef __clang__
#define sfc_inline gnu::always_inline, gnu::nodebug
#else
#define sfc_inline
#endif

namespace sfc {

using i8 = __INT8_TYPE__;
using i16 = __INT16_TYPE__;
using i32 = __INT32_TYPE__;
using i64 = __INT64_TYPE__;

using u8 = __UINT8_TYPE__;
using u16 = __UINT16_TYPE__;
using u32 = __UINT32_TYPE__;
using u64 = __UINT64_TYPE__;

using u128 = __uint128_t;
using i128 = __int128_t;

using usize = __UINTPTR_TYPE__;
using isize = __INTPTR_TYPE__;

using f32 = float;
using f64 = double;

struct Nil {};
static constexpr auto _ = Nil{};

template <auto I>
struct const_t {
  constexpr auto operator()() const {
    return I;
  }
};

template <template <class...> class I, class T>
[[sfc_inline]] auto as(const T& x) -> const I<T>& {
  static_assert(sizeof(T) == sizeof(I<T>));
  if constexpr (__is_class(T)) {
    return static_cast<const I<T>&>(x);
  } else {
    return reinterpret_cast<const I<T>&>(x);
  }
}

template <template <class...> class I, class T>
[[sfc_inline]] auto as_mut(T& x) -> I<T>& {
  static_assert(sizeof(T) == sizeof(I<T>));
  if constexpr (__is_class(T)) {
    return static_cast<I<T>&>(x);
  } else {
    return reinterpret_cast<I<T>&>(x);
  }
}

}  // namespace sfc
